﻿
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using EventArgs = System.EventArgs;
using OpenTK.Graphics;
using OpenTK;
using System.Threading;
using SkiaSharp;
using System.Diagnostics;
using WeifenLuo.WinFormsUI.Docking;
using LightCAD.Core;
using LightCAD.Runtime;
using LightCAD.Model;
using LightCAD.Three;
using LightCAD.Three.OpenGL;
using System.Timers;
using System.Drawing;
using Avalonia.Controls;

namespace LightCAD.UI
{
    public partial class Model3DEditWindow : DockContent, IModel3DEditControl
    {
        private GLControl glControl;
        private Runtime.IWindow hostWin;
        //[SuppressUnmanagedCodeSecurity]
        //[DllImport("OPENGL32.DLL", EntryPoint = "wglGetProcAddress", ExactSpelling = true, SetLastError = true)]
        //internal extern static IntPtr GetProcAddress(String lpszProc);
        [SuppressUnmanagedCodeSecurity]
        [DllImport("OPENGL32.DLL", EntryPoint = "wglGetProcAddress", ExactSpelling = true, SetLastError = true)]
        internal extern static IntPtr GetProcAddress(IntPtr lpszProc);

        internal event Action onInit;
        internal event Action onDestory;
        internal event Action<string, MouseEventRuntime> mouseEvent;
        internal event Action<KeyEventRuntime> keyDown;
        internal event Action<KeyEventRuntime> keyUp;
        internal event Action<Bounds> sizeChanged;
        internal event Action onRender;
        public event Action<List<LcLevel>> OnLevelFilterChanged;

        private System.Timers.Timer hoverTimer = new System.Timers.Timer();
        private static int HoverDelayTime = 500;//悬停延迟时间，毫秒
        private bool isPointerHovering = false;


        internal DocumentRuntime docRt;
        public IDocumentEditor Editor => Model3DEditRt;
        public CommandCenter CommandCenter => Model3DEditRt.CommandCenter;
        public Model3DEditRuntime Model3DEditRt { get; private set; }
        public Model3DEditWindow()
        {
            InitializeComponent();
            this.AutoScaleMode = AutoScaleMode.None;

            this.glControl = new GLControl();
            this.glControl.VSync = false;
            this.Controls.Add(this.glControl);
            this.glControl.Dock = DockStyle.Fill;
            this.Load += Model3DWindow_Load;
            this.HandleDestroyed += Model3DEditControl_HandleDestroyed;

            this.glControl.MouseDown += GlControl_MouseDown;
            this.glControl.MouseMove += GlControl_MouseMove;
            this.glControl.MouseUp += GlControl_MouseUp;
            this.glControl.KeyDown += GlControl_KeyDown;
            this.glControl.KeyUp += GlControl_KeyUp;
            this.glControl.MouseWheel += GlControl_MouseWheel;
            this.Enter += Model3DEditWindow_Enter;
        }

        private void Model3DEditWindow_Enter(object? sender, EventArgs e)
        {
            if (this.Visible)
                this.SetActive(true);
        }

        public Model3DEditWindow(DocumentRuntime docRt) : this()
        {
            this.docRt = docRt;
            this.Model3DEditRt = new Model3DEditRuntime(docRt, this, this.CommandBar);
            this.hoverTimer.Interval = HoverDelayTime;
            this.hoverTimer.Elapsed += HoverTimer_Elapsed;
        }

        private void GlControl_KeyUp(object? sender, KeyEventArgs e)
        {
            keyUp?.Invoke(e.ToRuntime());
        }

        private void GlControl_KeyDown(object? sender, KeyEventArgs e)
        {
            keyDown?.Invoke(e.ToRuntime());
        }

        private void GlControl_MouseUp(object? sender, MouseEventArgs e)
        {
            mouseEvent?.Invoke("Up", e.ToRuntime());
        }
        private MouseEventArgs moveEvent = null;
        private Point preHoverLocation;
        private void GlControl_MouseMove(object? sender, MouseEventArgs e)
        {
            this.moveEvent = e;
            mouseEvent?.Invoke("Move", this.moveEvent.ToRuntime());
            //模拟处理悬停事件，停在某点一段时间触发事件，移动光标悬停结束，等待下一次悬停事件
            //需要位置发生改变才停止Hover事件，因为ToolTip的显示，会导致Move事件的重复触发
            if (this.isPointerHovering && preHoverLocation != e.Location)
            {
                this.isPointerHovering = false;
                HoverEventEnd();
            }
            hoverTimer.Stop();
            hoverTimer.Start();
        }

        private void GlControl_MouseDown(object? sender, MouseEventArgs e)
        {
            mouseEvent?.Invoke("Down", e.ToRuntime());
        }
        private void GlControl_MouseWheel(object? sender, MouseEventArgs e)
        {
            mouseEvent?.Invoke("Wheel", e.ToRuntime());
        }

        private void HoverTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            hoverTimer.Stop();
            this.isPointerHovering = true;
            HoverEventStart();
        }
        private void HoverEventStart()
        {
            preHoverLocation = moveEvent.Location;
            mouseEvent?.Invoke("HoverStart", moveEvent.ToRuntime());
        }
        private void HoverEventEnd()
        {
            mouseEvent?.Invoke("HoverEnd", moveEvent.ToRuntime());
        }
        private void Model3DEditControl_HandleDestroyed(object? sender, EventArgs e)
        {
            try
            {
                //必须在this.glControl上下文dispose之前进行gl相关的dispose
                this.onDestory?.Invoke();
                this.glControl.Dispose();
            }
            catch (Exception ex) { }
        }

        public void AttachEvents(Action onInit, Action onDestory,
          Action<string, MouseEventRuntime> mouseEvent,
          Action<KeyEventRuntime> keyDown,
          Action<KeyEventRuntime> keyUp,
          Action<Bounds> sizeChanged,
          Action onRender)
        {
            if (onInit != null) this.onInit += onInit;
            if (onDestory != null) this.onDestory += onDestory;
            if (mouseEvent != null) this.mouseEvent += mouseEvent;
            if (keyDown != null) this.keyDown += keyDown;
            if (keyUp != null) this.keyUp += keyUp;
            if (sizeChanged != null) this.sizeChanged += sizeChanged;
            if (onRender != null) this.onRender += onRender;
        }

        public void DetachEvents(Action onInit, Action onDestory,
            Action<string, MouseEventRuntime> mouseEvent,
            Action<KeyEventRuntime> keyDown,
             Action<KeyEventRuntime> keyUp,
           Action<Bounds> sizeChanged,
            Action onRender)
        {
            if (onInit != null) this.onInit -= onInit;
            if (onDestory != null) this.onDestory -= onDestory;
            if (mouseEvent != null) this.mouseEvent -= mouseEvent;
            if (keyDown != null) this.keyDown -= keyDown;
            if (keyUp != null) this.keyUp -= keyDown;
            if (sizeChanged != null) this.sizeChanged -= sizeChanged;
            if (onRender != null) this.onRender -= onRender;
        }



        private void Model3DWindow_Load(object? sender, EventArgs e)
        {
            var context = GraphicsContext.CurrentContext;
            gl.GetAddressFunc = (fn) =>
            {
                var context_internal = context as IGraphicsContextInternal;
                var ptr = context_internal.GetAddress(fn);
                return ptr;
            };

            this.onInit?.Invoke();

            Control_SizeChanged(null, null);

            this.SizeChanged += Control_SizeChanged;
            //this.renderLoop=Task.Run(Render_Loop);
            this.Paint += Model3DEditControl_Paint;
            this.CommandCenter.Initilize();

        }

        private void CommandBtn_Click(object? sender, EventArgs e)
        {
            UIUtils.ExecuteCommand((sender as ToolStripItem).Name);
        }


        //private DateTime lastTime;
        //private int frameIndex;
        //public double FPS;
        //public string TitleExt;
        //public void CalFPS()
        //{
        //    if (lastTime == DateTime.MinValue)
        //    {
        //        lastTime = DateTime.Now;
        //    }
        //    frameIndex++;
        //    var delta = (DateTime.Now - lastTime).TotalSeconds;
        //    if (delta > 1)
        //    {
        //        FPS = Math.Round(frameIndex / delta, 1);
        //        lastTime = DateTime.Now;
        //        frameIndex = 0;
        //    }
        //}



        //public bool ShowFps { get; set; }

        //private Task renderLoop;
        //private void Render_Loop()
        //{
        //    while (!App.Current.IsExist && !this.IsStop)
        //    {
        //        Application.DoEvents();
        //        if (this.hostWin.IsActive)
        //        {
        //            this.Invoke(() => {
        //                this.OnRender();
        //            });
        //        }
        //        else
        //        {
        //            Thread.Sleep(10);
        //        }
        //    }
        //}

        private bool inRender;

        public Bounds EditorBounds => glControl.Bounds.ToRuntime();

        public EditorType EditorType => EditorType.Model3d;

        private void Model3DEditControl_Paint(object? sender, PaintEventArgs e)
        {
            //MainUIWindow Closing时会从窗口向子元素清理资源，导致GLControl刷新异常
            if (AppRuntime.IsClosing) return;

            if (inRender) return;
            this.inRender = true;

            if (this.glControl.IsDisposed) return;
            try
            {
                this.glControl.MakeCurrent();

                //if (ShowFps) CalFPS();

                this.onRender?.Invoke();

                this.glControl.SwapBuffers();

            }
            catch (Exception ex)
            {
            }
            this.inRender = false;
        }

        private void Control_SizeChanged(object? sender, EventArgs e)
        {
            this.glControl.Width = this.ClientRectangle.Width;
            this.glControl.Height = this.ClientRectangle.Height;
            this.sizeChanged?.Invoke(this.glControl.Bounds.ToRuntime());
            this.Model3DEditControl_Paint(null, null);
        }

        public void RefreshControl()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(() => { this.Model3DEditControl_Paint(null, null); });
            }
            else
            {
                this.Model3DEditControl_Paint(null, null);
            }
        }
        private LcDocument doc;

        private void btnSelectAllLevel_Click(object sender, EventArgs e)
        {
            this.dgvLevel.SelectAll();
        }

        private void dgvLevel_SelectionChanged(object sender, EventArgs e)
        {
            var selLevels = new List<LcLevel>();
            foreach (DataGridViewRow row in this.dgvLevel.SelectedRows)
            {
                var level = row.DataBoundItem as LcLevel;
                selLevels.Add(level);
            }
            OnLevelFilterChanged?.Invoke(selLevels);
        }
        private bool isLevelFilterShow = false;
        private void btnLevelShow_Click(object sender, EventArgs e)
        {
            if (isLevelFilterShow)
            {
                LvlPanel.Visible = false;
                LvlPanel.Dock = DockStyle.None;
                LvlPanel.Width = 56;
                LvlPanel.Height = 23;
            }
            else
            {
                LvlPanel.Visible = true;
                LvlPanel.Width = 107;
                LvlPanel.Dock = DockStyle.Left;
            }
            isLevelFilterShow = !isLevelFilterShow;
            this.RefreshControl();
        }

        private void btnSectionFrame_Click(object sender, EventArgs e)
        {
            this.Model3DEditRt.EnbaleClipBox();
        }

        private void btnSync3DViewport_Click(object sender, EventArgs e)
        {
            //this.Model3DEditRt.SyncViewPort2D(this.docRt.DrawingEditRt.ActiveViewportRt);
        }
        public void LoadDoc(LcDocument doc)
        {
            this.Invoke(new Action(() =>
            {
                //防止load多次刷新浪费性能
                this.dgvLevel.SelectionChanged -= dgvLevel_SelectionChanged;
                try
                {
                    if (doc.Buildings.Count == 0)
                    {
                        this.dgvLevel.Rows.Clear();
                    }
                    else
                    {
                        var building = doc.Buildings.FirstOrDefault();
                        var selLevels = new List<LcLevel>();
                        foreach (DataGridViewRow row in this.dgvLevel.SelectedRows)
                        {
                            var level = row.DataBoundItem as LcLevel;
                            selLevels.Add(level);
                        }
                        this.dgvLevel.AutoGenerateColumns = false;
                        this.dgvLevel.DataSource = building.Levels.ToList();
                        this.dgvLevel.ClearSelection();
                        this.doc = doc;
                        if (selLevels.Count > 0)
                        {
                            foreach (DataGridViewRow row in this.dgvLevel.Rows)
                            {
                                var level = row.DataBoundItem as LcLevel;
                                if (selLevels.Any(sl => sl == level))
                                    row.Selected = true;
                            }
                            OnLevelFilterChanged?.Invoke(selLevels);
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }
                finally
                {
                    this.dgvLevel.SelectionChanged += dgvLevel_SelectionChanged;
                }
            }));
        }
        public void SetActive(bool isActive)
        {
            if (isActive)
            {
                this.BringToFront();
                this.Enabled = true;
                //this.Activate();
                this.CommandCenter.SetActive();
            }
            else
            {
                this.SendToBack();
                this.Enabled = false;
            }
        }

        private void ControlsChange(object sender, EventArgs e)
        {
            string name = (sender as ToolStripItem).Name;
            var conType = Model.Model3DEditRuntime.ControlsTypeEnum.LightCAD;
            switch (name)
            {
                case "su":
                    conType = Model3DEditRuntime.ControlsTypeEnum.Su;
                    break;
                case "revit":
                    conType = Model3DEditRuntime.ControlsTypeEnum.Revit;
                    break;
                case "lightCAD":
                    conType = Model3DEditRuntime.ControlsTypeEnum.LightCAD;
                    break;
                default:
                    break;
            }
            this.Model3DEditRt.ChangeControls(conType);
        }
    }
}
